<?php
namespace Zaya\Lib\SSO;

use App\Http\Responses\ValidationResponse;
use Illuminate\Http\Request;
use Illuminate\Validation\ValidationException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Zaya\Lib\Exceptions\SimpleErrorException;

trait AuthenticatesServer
{
    /**
     * @param Request $request
     * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View|mixed
     * @throws SimpleErrorException
     * @throws ValidationException
     */
    public function gateway(Request $request)
    {
        $command = isset($_REQUEST['command']) ? $_REQUEST['command'] : null;

        if (!$command) {
            throw new NotFoundHttpException('Unknown Command');
        }
        $method = $request->getMethod();
        switch (true) {
            case $command === 'attach':
            case $command === 'logout':
            case $command === 'userInfo':
                return $this->$command();
            case $command === 'login':
                if ($method === 'GET') {
                    return $this->showLoginForm();
                } else {
                    return $this->login($request);
                }
            default:
                throw new NotFoundHttpException('Unknown Command: ' . $command);
        }
    }

    /**
     * 进行 SSO Client 与 SSO Server 绑定
     *
     * @return array|resource
     * @throws SimpleErrorException
     */
    public function attach()
    {
        return (new Server())->attach();
    }

    public function showLoginForm()
    {
        return view('sso.login');
    }

    /**
     * Authenticate
     *
     * @param Request $request
     * @return mixed
     * @throws SimpleErrorException|ValidationException
     */
    public function login(Request $request)
    {
        // 登录验证
        $params = $request->all();
        $v = \Validator::make($params, [
            'username' => 'required|min:4|max:20',
            'password' => 'required|min:6|max:20',
        ]);
        if ($v->fails()) {
            throw new ValidationException($v, new ValidationResponse());
        }
        $userInfo = $this->verificationCertificateAndReturnUserInfoOrFail($params['username'], $params['password']);
        try {
            \Cache::set('SSOUser:' . (new Server())->getServerToken(), $userInfo, Server::$loginAliveMinute);
        } catch (\Psr\SimpleCache\InvalidArgumentException $e) {
            throw new SimpleErrorException($e->getMessage());
        } catch (\Exception $e) {
            throw new SimpleErrorException($e->getMessage());
        }
        return $this->output(['user_info' => $userInfo]);
    }

    /**
     * 验证登录凭证，并返回用户信息或者错误
     *
     * @param string $username
     * @param string $password
     * @return mixed
     * @throws SimpleErrorException|ValidationException
     */
    abstract protected function verificationCertificateAndReturnUserInfoOrFail($username, $password);

    /**
     * @return array
     * @throws SimpleErrorException
     */
    public function logout()
    {
        try {
            $serverToken = (new Server())->getServerToken();

            \Cache::delete('SSOUser:' . $serverToken);

            $key = "SSOServerTokenHaveClientToken:" . $serverToken;
            $clientTokenArr = \Cache::pull($key);
            foreach ($clientTokenArr as $clientId => $token) {
                \Cache::delete("SSOClientTokenBindServerToken:{$clientId}-{$token}");
            }
        } catch (\Psr\SimpleCache\InvalidArgumentException $e) {
            throw new SimpleErrorException($e->getMessage());
        } catch (\Exception $e) {
            throw new SimpleErrorException($e->getMessage());
        }

        return $this->output(['退出成功']);
    }

    /**
     * Output user information as json.
     * @throws SimpleErrorException|NotLoginException|NotAttachedException|\Psr\SimpleCache\InvalidArgumentException
     */
    public function userInfo()
    {
        $clientId = \request('client_id');
        $clientToken = \request('client_token');
        $serverToken = (new Server())->getServerTokenByClientToken($clientId, $clientToken);
        if (is_null($serverToken)) {
            throw new NotAttachedException((new Server()), '未绑定');
        }
        $user = \Cache::get('SSOUser:' . $serverToken);
        if (is_null($user)) {
            throw new NotLoginException((new Server()), '未登录');
        }
        // 将登录时间延长
        \Cache::set('SSOUser:' . $serverToken, $user, Server::$loginAliveMinute);
        \Cache::set("SSOClientTokenBindServerToken:{$clientId}-{$clientToken}" , $serverToken, Server::$loginAliveMinute);
        return $user;
    }

    /**
     * Output Response
     * @param array $params
     * @return array
     */
    public function output($params)
    {
        if (request('callback')) {
            return response()->jsonp(request('callback'), $params);
        }
        if (request('return_url')) {
            return redirect(request('return_url'));
        }
        return $params;
    }
}